package com.neo.tiny.admin.service.sys.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.neo.tiny.admin.api.user.UserAuthApi;
import com.neo.tiny.admin.entity.sys.SysUser;
import com.neo.tiny.admin.params.auth.UserProfilePasswordUpdateReq;
import com.neo.tiny.admin.service.sys.SysUserService;
import com.neo.tiny.admin.vo.user.AuthUserDetails;
import com.neo.tiny.common.constant.CacheConstants;
import com.neo.tiny.common.constant.ErrorCodeConstants;
import com.neo.tiny.common.enums.UserTypeEnum;
import com.neo.tiny.common.exception.WebApiException;
import com.neo.tiny.oauth.api.Oauth2TokenApi;
import com.neo.tiny.oauth.util.OAuth2Utils;
import com.neo.tiny.query.LambdaQueryWrapperBase;
import com.neo.tiny.secrity.util.SecurityUtils;
import com.neo.tiny.service.JwtTokenUtil;
import com.neo.tiny.service.RedisService;
import com.neo.tiny.token.store.OAuth2AccessToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Description:
 * @Author: yqz
 * @CreateDate: 2022/8/7 14:01
 */
@Slf4j
@Service
public class SysAuthService {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private SysUserService userService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private Oauth2TokenApi oauth2TokenApi;

    @Autowired
    private UserAuthApi userAuthApi;


//    /**
//     * 使用security做校验
//     * @param username
//     * @param password
//     * @return
//     */
//    public String usernameLogin(String username, String password) {
//        // 1 创建UsernamePasswordAuthenticationToken
//        UsernamePasswordAuthenticationToken authenticationToken =
//                new UsernamePasswordAuthenticationToken(username, password);
//        // 2 认证。调用UserDetailsServiceImpl.loadUserByUsername
//        Authentication authenticate = null;
//        try {
//            authenticate = this.authenticationManager.authenticate(authenticationToken);
//        } catch (Exception e) {
//            if (e instanceof BadCredentialsException) {
//                log.info("{}:密码错误", username);
//            } else {
//                log.info("{}:用户名错误", username);
//            }
//            throw new WebApiException("用户名或密码错误");
//        }
//        // 3 保存认证信息
//        SecurityContextHolder.getContext().setAuthentication(authenticate);
//        // 4 生成自定义token
//        return jwtTokenUtil.generateToken((UserDetails) authenticate.getPrincipal());
//    }



    /**
     * 自定义登录校验
     *
     * @param username 用户名
     * @param password 密码
     * @return
     */
    public OAuth2AccessToken usernameLogin(String username, String password, HttpServletRequest request) {

        String[] clientId = OAuth2Utils.obtainBasicAuthorization(request);
        AuthUserDetails authenticate = userAuthApi.authenticate(username, password);
        return oauth2TokenApi.createAccessToken(authenticate.getUserId(),authenticate.getUsername()
                ,UserTypeEnum.ADMIN.getValue(), clientId[0], new ArrayList<>());
    }

    /**
     * 短信验证码登录
     *
     * @param phone 手机号
     * @param code  验证码
     * @return jwt
     */
    public OAuth2AccessToken smsLogin(String phone, String code, HttpServletRequest request) {

        Object codeCache = redisService.get(CacheConstants.SMS_LOGIN_CODE + StrUtil.COLON + phone);

        if (Objects.isNull(codeCache)) {
            throw new WebApiException(ErrorCodeConstants.USER_PHONE_LOGIN_ERROR);
        }
        if (!StrUtil.equals(codeCache.toString(), code)) {
            throw new WebApiException(ErrorCodeConstants.USER_PHONE_LOGIN_ERROR);
        }

        SysUser sysUser = userService.getOne(new LambdaQueryWrapperBase<SysUser>().eq(SysUser::getPhone, phone));
        UserDetails userDetails = userDetailsService.loadUserByUsername(sysUser.getUserName());

        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,
                null, userDetails.getAuthorities());

        SecurityContextHolder.getContext().setAuthentication(authentication);

        String[] clientId = OAuth2Utils.obtainBasicAuthorization(request);

        // 生成自定义token
        return oauth2TokenApi.createAccessToken(sysUser.getId(), sysUser.getUserName(),
                UserTypeEnum.ADMIN.getValue(), clientId[0], new ArrayList<>());
    }

    /**
     * 发送验证码
     *
     * @param phone 手机号
     * @return
     */
    public String sendLoginSmsCode(String phone) {
        // TODO 校验手机号发送次数
        List<SysUser> list = userService.list(new LambdaQueryWrapperBase<SysUser>().eq(SysUser::getPhone, phone));
        if (list.size() == 0) {
            throw new WebApiException(ErrorCodeConstants.USER_PHONE_NOT_EXIST);
        }
        String code = RandomUtil.randomString("0123456789", 6);

        // 放入缓存 两分钟
        redisService.set(CacheConstants.SMS_LOGIN_CODE + StrUtil.COLON + phone, code, 60 * 2, TimeUnit.SECONDS);
        log.info("手机号：{},验证码：{}", phone, code);
        // TODO 待接入SMS服务后为用户发送短信

        // TODO 暂时将code返回接口，便于测试
        return code;
    }

    public void logout(HttpServletRequest request) {

        String token = SecurityUtils.obtainHeaderToken(request);
        oauth2TokenApi.removeAccessToken(token);

        // 退出登录后，清除用户信息缓存
        String username = SecurityUtils.getUserName();
        String key = CacheConstants.USER_DETAILS + StrUtil.COLON + username;
        redisService.del(key);
    }

    /**
     * 修改个人用户密码
     *
     * @param userId 用户id
     * @param req    入参
     */
    public void updateUserPassword(Long userId, UserProfilePasswordUpdateReq req) {
        checkOldPassword(userId, req.getOldPassword());
        SysUser user = new SysUser();
        user.setId(userId);
        user.setPassword(passwordEncoder.encode(req.getNewPassword()));
        userService.updateById(user);
    }

    /**
     * 校验旧密码
     *
     * @param userId      用户id
     * @param oldPassword 旧密码
     */
    private void checkOldPassword(Long userId, String oldPassword) {

        SysUser user = userService.getById(userId);
        if (Objects.isNull(user)) {
            throw new WebApiException("该用户不存在");
        }
        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            throw new WebApiException("旧密码校验失败");
        }
    }

}
